﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using Abp.AutoMapper;
using Abp.Domain.Repositories;
using Abp.Domain.Uow;
using Abp.Events.Bus.Entities;
using Abp.ObjectMapping;
using Abp.Runtime.Caching;
using Abp.Runtime.Session;
using Hicap.AuthorizationServer.Core.Caches.Sys.Interface;
using Hicap.AuthorizationServer.Core.Models.Sys;
using Microsoft.EntityFrameworkCore;

namespace Hicap.AuthorizationServer.Core.Caches.Sys
{
    public class UserAuthCache : IUserAuthCache
    {
        private readonly IRepository<UserRoleRelated> _userRoleRepository;
        private readonly IRepository<AuthObjectRoleRelated> _authRoleRepository;
        private readonly IRepository<AuthObject> _authRepository;
        private readonly IUserCache _userCache;
        private readonly IAbpSession _abpSession;
        private readonly IUnitOfWorkManager _unitOfWorkManager;
        public UserAuthCacheItem this[int id] => Get(id);
        public ITypedCache<int, UserAuthCacheItem> InternalCache => CacheManager.GetCache<int, UserAuthCacheItem>(CacheName);
        public string CacheName { get; private set; }
        public IObjectMapper ObjectMapper { get; set; }
        protected ICacheManager CacheManager { get; private set; }

        public UserAuthCache(ICacheManager cacheManager,
            IRepository<UserRoleRelated> userRoleRepository,
            IRepository<AuthObjectRoleRelated> authRoleRepository,
            IAbpSession abpSession, IUserCache userCache, IRepository<AuthObject> authRepository, IUnitOfWorkManager unitOfWorkManager)
        {
            CacheManager = cacheManager;
            _userRoleRepository = userRoleRepository;
            _authRoleRepository = authRoleRepository;
            _abpSession = abpSession;
            _userCache = userCache;
            _authRepository = authRepository;
            _unitOfWorkManager = unitOfWorkManager;
            CacheName = "UserAuthCache";
            ObjectMapper = NullObjectMapper.Instance;
        }

        public UserAuthCacheItem Get(int id)
        {
            return InternalCache.Get(id, () => GetFromDatabase(id));
        }

        public Task<UserAuthCacheItem> GetAsync(int id)
        {
            return Task.Run(() => Get(id));
        }

        protected void HandleEvent<T>(EntityChangedEventData<T> eventData)
        {
            var reloadFlag = false;
            var userId = Convert.ToInt32(_abpSession.UserId.Value);
            var currentCache = Get(userId);

            if (typeof(T) == typeof(UserRoleRelated))
            {
                reloadFlag = true;
            }
            else if (typeof(T) == typeof(AuthObjectRoleRelated))
            {
                var authObjectRoleRelated = eventData.Entity as AuthObjectRoleRelated;
                if (currentCache.Roles.Select(x => x.Id).ToList().Contains(authObjectRoleRelated.Role.Id))
                {
                    reloadFlag = true;
                }
            }
            else
            {
                return;
            }

            if (!reloadFlag) return;

            var cache = GetFromDatabase(userId);
            InternalCache.Set(userId, cache);
        }

        private UserAuthCacheItem GetFromDatabase(int id)
        {
            var result = new UserAuthCacheItem();
            //... 从数据库中检索数据
            var roles = _userRoleRepository.GetAllIncluding(x => x.User, x => x.Role)
                .AsNoTracking()
                .Where(x => x.User.Id == id)
                .Select(x => x.Role);

            var auths = _authRoleRepository.GetAllIncluding(x => x.Role, x => x.AuthObject)
                .AsNoTracking()
                .Where(x => roles.Select(y => y.Id).Any(y => x.Role.Id == y))
                .Select(x => x.AuthObject).ToList();

            result.Roles = roles.MapTo<List<RoleCacheItem>>();
            result.AuthObjects = auths.MapTo<List<AuthObjectCacheItem>>();
            result.User = _userCache.Get(id);

            return result;
        }
    }
}